Strategy Pattern(스트래티지 패턴)

들어가기

이번 포스팅에서는 흔히 스트래티지 패턴(Strategy Pattern)으로 불리는 전략 패턴 에 대해서 알아보겠습니다.

본론

먼저 본격적인 설명에 앞서 하나의 예시를 보겠습니다. 우리는 앞으로 두가지 타입의 로봇을 만들 예정입니다. 걸어다니고 미사일로 공격하는 태권V와 날아다니며 주먹으로 공격하는 아톰이라는 로봇입니다.

가장 쉽게 생각할 수 있는 방법은 Robot이라는 추상 클래스를 만들고 이를 상속하는 각각의 로봇을 만들어 로봇 타입에 맞는 방법으로 메서드를 재정의하는 방법입니다. 즉, 밑에 보여지는 다이어그램 같은 방법입니다.

다시 이 다이어그램을 코드로 옮겨보겠습니다.

Robot.java

public abstract class Robot {
    private String name;

    public Robot(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public abstract void attack();
    public abstract void move();
}

TaekwonV.java

public class TaekwonV extends Robot {
    public TaekwonV(String name) {
        super(name);
    }

    @Override
    public void attack() {
      System.out.println("Missile Attack");
    }

    @Override
    public void move() {
      System.out.println("Walk");
    }
}

Atom.java

public class Atom extends Robot {
    public Atom(String name) {
        super(name);
    }

    @Override
    public void attack() {
      System.out.println("Punch Attack");
    }

    @Override
    public void move() {
      System.out.println("Fly");
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Robot taekwonV = new TaekwonV("TaewonV");
        Robot atom = new Atom("atom");

        System.out.println(taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println("-------------------------------------------------------");

        System.out.println(atom.getName());
        atom.attack();
        atom.move();
    }
}

Main.java를 실행하여 출력물을 확인하면 우리가 구현하고자하는 로봇 타입을 다음과 같이 출력합니다.

하지만 위와 같은 설계는 문제점을 야기할 수 있는 설계입니다. 어떤점이 문제일까요?

만약 Atom과 완전 똑같은 기능을 수행하는 다른 로봇인 Mazinger를 추가하려면 어떻해야 할까요? 클래스를 하나 더 만들어 Robot 클래스를 상속하고 기능을 Atom과 똑같이 구현하면 문제는 해결됩니다.

여기서 특정 기능을 똑같이 필요로 하는 다른 로봇들이 많이 추가된다면 위와 같은 설계는 코드 중복을 야기합니다. 또 이에 따라 해당 기능이 오류가 있어 수정했다면 모든 중복된 코드를 일관성 있게 변경해야합니다.

과연 어떻게 위와 같은 문제를 해결할 수 있을까요??

다른 로봇에 의해 자주 사용되고 수정되는 attack와 move 메서드를 추상화해 문제를 해결할 수 있습니다.

그럼 이전 다이어그램과 비교할때 어떻게 바뀌었을까요?

다소 복잡해지긴 했지만 무엇이 변화될것인지를 찾은 후에 이를 캡슐화한 과정입니다.

이와 같은 설계는 로봇 객체와는 상관없이 향후 등장할 이동방식과 공격방식의 변화뿐만 아니라 현재 변화도 잘 처리할 수 있게 만든 설계입니다.

변경점을 바탕으로 다시 코드를 수정해보겠습니다.

AttackStrategy.java

public interface AttackStrategy {
    public void attack();
}

missileattack.java

public class missileattack implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("Missile Attack");
    }
}

punchattack.java

public class punchattack implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("Punch Attack");
    }
}

MoveStrategy.java

public interface MoveStrategy {
    public void move();
}

flymove.java

public class flymove implements MoveStrategy {
    @Override
    public void move() {
        System.out.println("Fly");
    }
}

walkmove.java

public class walkmove implements MoveStrategy {
    @Override
    public void move() {
        System.out.println("Walk");
    }
}

Robot.java

public abstract class Robot {
    private String name;
    private MoveStrategy mv;
    private AttackStrategy at;

    public Robot(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void move() {
        mv.move();
    }

    public void attack() {
        at.attack();
    }

    public void setMove(MoveStrategy move) {
        this.mv = move;
    }

    public void setAttack(AttackStrategy attack) {
        this.at = attack;
    }
}

Atom.java

public class Atom extends Robot {
    public Atom(String name) {
        super(name);
    }
}

TaekwonV.java

public class TaekwonV extends Robot {
    public TaekwonV(String name) {
        super(name);
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Robot taekwonV = new TaekwonV("TaewonV");
        taekwonV.setAttack(new missileattack());
        taekwonV.setMove(new walkmove());
        Robot atom = new Atom("atom");
        atom.setAttack(new punchattack());
        atom.setMove(new flymove());

        System.out.println(taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println("-------------------------------------------------------");

        System.out.println(atom.getName());
        atom.attack();
        atom.move();
    }
}

새롭게 설계한 로봇은 이전 설계했던 로봇과 똑같은 결과값을 출력합니다. 하지만 이전 코드와 비교해 볼때 앞으로 발생할 코드의 중복을 줄일수 있게 되었고 또 현재 로봇들의 기능을 손쉽게 변경할수있게 됐습니다.

스트래티지 패턴(Strategy Pattern), 전략 패턴은 이와 같이 전략을 쉽게 바꿀 수 있도록 해주는 디자인 패턴입니다.

여기에서 전략이란 어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘 등으로 이해할 수 있습니다.

즉, 스트래티지 패턴(Strategy Pattern), 전략 패턴은 같은 문제를 해결하는 여러 알고리즘이 클래스 별로 캡슐화되어 있고 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게 하는 디자인 패턴입니다.

마치며

스트래티지 패턴(Strategy Pattern), 전략 패턴의 개념과 관련해 추가적인 질문이나 오류, 오타가 있을시 댓글로 남겨주세요.

출처

JAVA 객체지향 디자인패턴

Share